package net.xdclass.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.TimeConstant;
import net.xdclass.controller.request.ConfirmOrderRequest;
import net.xdclass.controller.request.ProductOrderPageRequest;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.manager.ProductManager;
import net.xdclass.manager.ProductOrderManager;
import net.xdclass.model.EventMessage;
import net.xdclass.model.LoginUser;
import net.xdclass.model.ProductDO;
import net.xdclass.model.ProductOrderDO;
import net.xdclass.service.ProductOrderService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import net.xdclass.util.JsonUtil;
import net.xdclass.vo.PayInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderManager productOrderManager;
    @Autowired
    private ProductManager productManager;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private PayFactory payFactory;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Map<String, Object> page(ProductOrderPageRequest request) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        return productOrderManager.page(request.getPage(),request.getSize(),accountNo, request.getState());
    }

    @Override
    public String queryState(String outTradeNo) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        if(productOrderDO == null){
            return null;
        }
        return productOrderDO.getState();
    }

    @Override
    @Transactional
    public JsonData confirmOrder(ConfirmOrderRequest request) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);

        ProductDO productDO = productManager.findDetailById(request.getProductId());

        //验证价格
        this.checkPrice(productDO, request);

        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(request, loginUser, orderOutTradeNo, productDO);

        //创建支付对象
        PayInfoVO payInfoVO = PayInfoVO.builder().accountNo(loginUser.getAccountNo())
                .outTradeNo(orderOutTradeNo)
                .clientType(request.getClientType())
                .payType(request.getPayType())
                .title(productDO.getTitle())
                .payFee(request.getPayAmount())
                .orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS)
                .build();

        //发送延迟消息
        EventMessage eventMessage = EventMessage.builder()
                .eventMessageType(EventMessageType.PRODUCT_ORDER_NEW.name())
                .accountNo(loginUser.getAccountNo())
                .bizId(orderOutTradeNo)
                .build();

        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), eventMessage);

        //调用支付信息
        String codeUrl = payFactory.pay(payInfoVO);
        if (StringUtils.isNotBlank(codeUrl)) {
            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("code_url", codeUrl);
            resultMap.put("out_trade_no", payInfoVO.getOutTradeNo());
            return JsonData.buildSuccess(resultMap);
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
    }

    @Override
    public boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        if (productOrderDO == null) {
            //订单不存在
            log.warn("订单不存在");
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            //已经支付
            log.info("直接确认消息，订单已经支付:{}", eventMessage);
            return true;
        }
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            //向第三方查询状态
            PayInfoVO payInfoVO = new PayInfoVO();
            payInfoVO.setPayType(productOrderDO.getPayType());
            payInfoVO.setAccountNo(accountNo);
            payInfoVO.setOutTradeNo(outTradeNo);

            //向第三方查询状态
            String payResult = "";
            if (StringUtils.isBlank(payResult)) {
                productOrderManager.updateOrderPayState(outTradeNo,accountNo, ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
                log.info("未支付成功，本地取消订单:{}", eventMessage);
            } else {
                //支付成功
                log.warn("支付成功，但是微信回调失败", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo,accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData processOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {
        //获取商户订单号
        String outTradeNo = paramsMap.get("out_trade_no");
        //交易状态
        String tradeState = paramsMap.get("trade_state");

        Long accountNo = Long.valueOf(paramsMap.get("account_no"));

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        Map<String, Object> content = new HashMap<>();
        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", accountNo);
        content.put("product", productOrderDO.getProductSnapshot());

        //构建消息
        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(accountNo)
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
                .build();

        if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())) {
            //支付宝支付

        } else if(payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())){
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {

                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);

                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                            rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
                    return JsonData.buildSuccess();
                }
            }
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    @Override
    public void handleProductOrderMessage(EventMessage eventMessage) {
        String messageType = eventMessage.getEventMessageType();
        try {
            if (EventMessageType.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) {
                //关闭订单
                this.closeProductOrder(eventMessage);
            } else if(EventMessageType.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)){
                String outTradeNo = eventMessage.getBizId();
                Long accountNo = eventMessage.getAccountNo();
                int rows = productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                log.info("订单更新成功:rows={},eventMessage={}", rows, eventMessage);
            }
        } catch (Exception e) {
            log.error("订单消费者消费失败:{}", eventMessage, e);
            throw new BizException(BizCodeEnum.MQ_CONSUME_EXCEPTION);
        }
    }

    private ProductOrderDO saveProductOrder(ConfirmOrderRequest request, LoginUser loginUser, String orderOutTradeNo, ProductDO productDO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        //设置用户信息
        productOrderDO.setAccountNo(loginUser.getAccountNo());
        productOrderDO.setNickname(loginUser.getUsername());
        //设置商品信息
        productOrderDO.setProductId(productDO.getId());
        productOrderDO.setProductTitle(productDO.getTitle());
        productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));
        productOrderDO.setProductAmount(productDO.getAmount());

        //设置订单信息
        productOrderDO.setBuyNum(request.getBuyNum());
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        //发票信息
        productOrderDO.setBillType(BillTypeEnum.valueOf(request.getBillType()).name());
        productOrderDO.setBillHeader(request.getBillHeader());
        productOrderDO.setBillReceiverPhone(request.getBillReceiverPhone());
        productOrderDO.setBillReceiverEmail(request.getBillReceiverEmail());
        productOrderDO.setBillContent(request.getBillContent());

        //实际支付总价
        productOrderDO.setPayAmount(request.getPayAmount());
        //总价，没使用优惠券
        productOrderDO.setTotalAmount(request.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(request.getPayType()).name());

        //插入数据库
        productOrderManager.add(productOrderDO);
        return productOrderDO;
    }

    private void checkPrice(ProductDO productDO, ConfirmOrderRequest request) {
        //计算价格
        BigDecimal bizTotal = BigDecimal.valueOf(request.getBuyNum()).multiply(productDO.getAmount());

        if (bizTotal.compareTo(request.getPayAmount())!=0) {
            log.error("验证价格失败{}", request);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }
}
